package com.android.webserver.tornado;

import java.net.*;
import java.io.*;
import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.NoSuchElementException;

import android.net.UrlQuerySanitizer;
import android.util.Log;

public class Request
{
	private final static int									BUFFER_SIZE		= 4096;
	private final static int									HEADER_LENGTH	= 20;

	private final transient Socket						socket;
	private final transient InputStreamReader	input;
	private final HashMap<String, String>			headers				= new HashMap<String, String>();
	private HashMap<String, String>						cookies				= new HashMap<String, String>();

	private float															protocolVersion;
	private String														requestURI;
	private String														requestType;
	private String														requestURL;																		// without
	// query
	private String														requestQuery;
	private String														rawRequest;
	private String														rawHeaders;
	private String														rawBody;
	UrlQuerySanitizer												uqs;

	public Request(Socket client) throws IOException
	{
		socket = client;
		BufferedInputStream in = new BufferedInputStream(socket.getInputStream(),
				BUFFER_SIZE);
		input = new InputStreamReader(in, "ASCII");
		rawHeaders = "";
		rawBody = "";
		requestQuery = "";
	}
	
	public void
	readRequest() throws HTTPException, IOException
	{
		readHeaders();
		if (getType().equals("POST"))
			readBody(input);
		uqs = new UrlQuerySanitizer();
		uqs.setAllowUnregisteredParamaters(true);
		if (requestType.equals("GET"))
			uqs.parseUrl(requestURI);
		else
			if (requestType.equals("POST"))
				uqs.parseQuery(rawBody);
	}

	private void readHeaders() throws IOException, HTTPException
	{
		// read the request line
		processRequest(readLine(input, true));

		// read the rest of the headers
		while (true)
		{
			String header = readLine(input, false);
			if (header.equals(""))
				break;
			else
				processHeader(header);
		}
	}

	private String readLine(Reader r, boolean multiLine) throws IOException
	{
		StringBuffer buffer = new StringBuffer(HEADER_LENGTH);
		while (true)
		{
			char c = (char) r.read();

			if (c == '\r')
				continue;
			else
				if (c == '\n')
				{
					if (buffer.length() == 0 && multiLine)
						continue;
					else
						return buffer.toString();
				}
				else
					if (c == -1)
						return buffer.toString();
					else
						buffer.append(c);
		}
	}

	private void processRequest(String request) throws IOException, HTTPException
	{
		rawRequest = request;

		StringTokenizer st = new StringTokenizer(request);		
		formatType(st);
		formatURI(st);
		formatProtocolVersion(st);
	}

	private void formatType(StringTokenizer st)
	{
		requestType = st.nextToken().toUpperCase();
	}

	private void formatURI(StringTokenizer st) throws HTTPException
	{
		String uri = null;
		String urib = null;
		try
		{
			urib = st.nextToken();
			Log.d("WebServer", urib + ":=> XXX" );
			uri = URLDecoder.decode(urib, "UTF-8");
			Log.d("WebServer", urib + ":=>" + uri);
		}
		catch (Exception e)
		{
			e.printStackTrace();
			throw new HTTPException(HTTP.BAD_REQUEST);
		}
		
		

		if (!uri.startsWith("http://"))
		{
			uri = "http://localhost:8080" + uri;
		}
		requestURI = uri;

		int split = uri.indexOf('?');
		if (split > 0)
		{
			requestURL = uri.substring(0, split);
			requestQuery = uri.substring(split + 1);
		}
		else
			requestURL = uri;
	}

	private void formatProtocolVersion(StringTokenizer st)
	{
		/*
		 * Be careful: we need to handle legacy clients who do not send an HTTP
		 * version.
		 */
		String str;
		try
		{
			str = st.nextToken();
		}
		catch (NoSuchElementException e)
		{
			str = "";
		}

		if (str.endsWith("1.1"))
			protocolVersion = (float) 1.1;
		else
			if (str.endsWith("1.0"))
				protocolVersion = (float) 1.0;
			else
				protocolVersion = (float) 0.9; // default to 0.9
	}

	private void processHeader(String header)
	{
		int split = header.indexOf(':', 3);
		if (split == -1)
			return; // ignore malformed headers

		String key = header.substring(0, split);
		String val = header.substring(split + 1).trim();
		headers.put(key, val);
		rawHeaders += header + "\n";

		if (key.equals("Cookie"))
			processCookieHeader(val);
	}

	private void processCookieHeader(String cookie)
	{
		String[] cookieFields = cookie.split(";");
		for (int i = 0; i < cookieFields.length; i++)
		{
			int split = cookieFields[i].indexOf('=');
			if (split != -1)
			{
				String key = cookieFields[i].substring(0, split);
				String value = cookieFields[i].substring(split + 1);
				cookies.put(key, value);
			}
		}
	}

	private void readBody(Reader r) throws IOException
	{
		int contentLength = Integer.parseInt(getHeader("Content-Length")
				.replaceAll(":", "").trim());
		rawBody = "";
		for (int i = 0; i < contentLength; i++)
			rawBody += (char) r.read();
		if (requestType.equals("POST"))
			requestQuery = rawBody;
	}

	public Socket getSocket()
	{
		return socket;
	}

	public String getHostAddress()
	{
		return socket.getInetAddress().getHostAddress();
	}

	public int getPort()
	{
		return socket.getPort();
	}

	public float getProtocolVersion()
	{
		return protocolVersion;
	}

	public String getURI()
	{
		return requestURI;
	}

	public String getType()
	{
		return requestType;
	}

	// will never include CRLF
	public String getRawRequest()
	{
		return rawRequest;
	}

	public String getRawHeaders()
	{
		return rawHeaders;
	}

	public String getHeader(String key)
	{
		return (String) headers.get(key);
	}

	public String getRawBody()
	{
		return rawBody;
	}

	public String getRequestURL()
	{
		return requestURL;
	}

	public String getRequestQuery()
	{
		return requestQuery;
	}

	public String getAppPath()
	{
		String fullPath = getFullPath();

		int nextPathPost = fullPath.indexOf('/', 1);
		int nextParamPost = fullPath.indexOf('?', 1);

		if (nextPathPost > -1)
			return fullPath.substring(1, nextPathPost);
		else
			if (nextParamPost > -1)
				return fullPath.substring(1, nextParamPost);
			else
				return fullPath.substring(1);
	}

	public String getFullPath()
	{
		int start = rawRequest.indexOf(' ');
		int end = rawRequest.indexOf('?', start + 1);
		if (end == -1)
			end = rawRequest.indexOf(' ', start + 1);
		return rawRequest.substring(start + 1, end);
	}

	public String getCookie(String key)
	{
		return cookies.get(key);
	}
	
	public String getParameter(String parameter)
	{
		return uqs.getValue(parameter);
	}


	public void dump()
	{
		Log.d("WebServer", "==== RAWRequest ====\n " + rawRequest + " ======");

		Log.d("WebServer", "==== URI : " + getURI());
		Log.d("WebServer", "==== TYPE : " + getType());

		Log.d("WebServer", "==== HEADERS ====");
		for (String key : headers.keySet())
			Log.d("WebServer", key + " = " + headers.get(key));
	}

}
